﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Input;
using System.Collections;

namespace Tetris
{
    class Playfield
    {
        #region --- Properties ---

        ContentManager content;
        private const int _width = 10;
        private const int _height = 22;
        private List<Piece> _pieces;
        private Piece _currentPiece;
        private Piece _nextPiece;
        private Piece _holdPiece = null;
        private int ghostPieceDiffY = 0;
        int _numberOfCycles = 0;
        private const int INITSPEED = 50;
        public int _PIECE_SPEED = INITSPEED;
        private int _SPEEDUP_INCREMENT = 3;
        private int numTetrisInRow = 0;
        private int numRowsCleared = 0;
        private const int STARTING_GRID_X = 346;
        private const int STARTING_GRID_Y = 23;
        private const string BLANK_SQUARE = "Images/emptySquare2";
        private const string GHOST_SQUARE = "Images/ghostSquare";

        private int _topGridBuffer = 2;

        public Square[,] grid { get; set; }

        Game game;

        #endregion

        #region --- Constructor ---

        public Playfield(Game game)
        {
            this.game = game;
            grid = new Square[_width, _height];
            nextPieceGrid = new Square[NEXT_PIECE_GRID_WIDTH, NEXT_PIECE_GRID_HEIGHT];
            holdPieceGrid = new Square[HOLD_PIECE_GRID_WIDTH, HOLD_PIECE_GRID_HEIGHT];
        }

        #endregion

        #region --- Initialization ---

        public void LoadContent()
        {
            content = game.Content;
        }

        public void InitializeGrid()
        {
            ResetSpeed();
            initializeHoldPieceGrid();
            initializeNextPieceGrid();
            for(int i = 0; i< _width; i++)
            {
                for (int j = 0; j < _height; j++)
                {
                    Square square = new Square();
                    grid[i, j] = square;
                    square.gridX = i;
                    square.gridY = j;
                    square.isEmpty = true;
                    if (square.gridY >= _topGridBuffer)
                    {
                        square.Position = new Vector2((i - _topGridBuffer) * Square.Width, (j - _topGridBuffer) * Square.Height);
                        square.background = content.Load<Texture2D>(BLANK_SQUARE) as Texture2D;
                    }
                }
            }
            if(_nextPiece == null)
                LoadRandomPiece();
        }

        #endregion

        #region --- Next Piece ---

        public Square[,] nextPieceGrid { get; set; }
        private const int NEXT_PIECE_GRID_WIDTH = 4;
        private const int NEXT_PIECE_GRID_HEIGHT = 4;
        private const int NEXT_PIECE_ADJUSTED_GRID_X = 3;


        public void initializeNextPieceGrid()
        {
            for (int i = 0; i < NEXT_PIECE_GRID_WIDTH; i++)
            {
                for (int j = 0; j < NEXT_PIECE_GRID_HEIGHT; j++)
                {
                    Square square = new Square();
                    nextPieceGrid[i, j] = square;
                    square.gridX = i;
                    square.gridY = j;
                    square.isEmpty = true;
                    square.Position = new Vector2(i * Square.Width, j * Square.Height);
                    square.background = content.Load<Texture2D>(BLANK_SQUARE) as Texture2D;
                    square.Color = Color.White;
                }
            }
        }

        public void PlacePieceOnNextPieceGrid()
        {
            initializeNextPieceGrid();
            foreach (Square square in _nextPiece.squares)
            {
                nextPieceGrid[square.gridX - NEXT_PIECE_ADJUSTED_GRID_X, square.gridY] = square;
            }
        }

        public void UpdateNextPieceGrid()
        {
            for (int i = 0; i < NEXT_PIECE_GRID_WIDTH; i++)
            {
                for (int j = 0; j < NEXT_PIECE_GRID_HEIGHT; j++)
                {
                    Square square = nextPieceGrid[i, j];
                    if (square.isEmpty)
                    {
                        square.Color = Color.White;
                        square.background = content.Load<Texture2D>(BLANK_SQUARE) as Texture2D;
                        square.Position = new Vector2((square.gridX) * Square.Width, Math.Abs(square.gridY) * Square.Height);
                    }
                    else
                    {
                        square.background = content.Load<Texture2D>(square.backgroundString) as Texture2D;
                        square.Position = new Vector2((square.gridX - NEXT_PIECE_ADJUSTED_GRID_X) * Square.Width, Math.Abs(square.gridY) * Square.Height);
                    }
                }
            }
        }

        #endregion

        #region --- Hold Piece ---

        public Square[,] holdPieceGrid { get; set; }
        private const int HOLD_PIECE_GRID_WIDTH = 4;
        private const int HOLD_PIECE_GRID_HEIGHT = 4;


        public void initializeHoldPieceGrid()
        {
            for (int i = 0; i < HOLD_PIECE_GRID_WIDTH; i++)
            {
                for (int j = 0; j < HOLD_PIECE_GRID_HEIGHT; j++)
                {
                    Square square = new Square();
                    holdPieceGrid[i, j] = square;
                    square.gridX = i;
                    square.gridY = j;
                    square.isEmpty = true;
                    square.Position = new Vector2(i * Square.Width, j * Square.Height);
                    square.background = content.Load<Texture2D>(BLANK_SQUARE) as Texture2D;
                    square.Color = Color.White;
                }
            }
        }

        public void PlacePieceOnHoldPieceGrid()
        {
            initializeHoldPieceGrid();
            foreach (Square square in _holdPiece.squares)
            {
                holdPieceGrid[square.gridX, square.gridY] = square;
            }
        }

        public void UpdateHoldPieceGrid()
        {
            for (int i = 0; i < HOLD_PIECE_GRID_WIDTH; i++)
            {
                for (int j = 0; j < HOLD_PIECE_GRID_HEIGHT; j++)
                {
                    Square square = holdPieceGrid[i, j];
                    if (square.isEmpty)
                    {
                        square.Color = Color.White;
                        square.background = content.Load<Texture2D>(BLANK_SQUARE) as Texture2D;
                        square.Position = new Vector2((square.gridX) * Square.Width, Math.Abs(square.gridY) * Square.Height);
                    }
                    else
                    {
                        square.background = content.Load<Texture2D>(square.backgroundString) as Texture2D;
                        square.Position = new Vector2((square.gridX) * Square.Width, Math.Abs(square.gridY) * Square.Height);
                    }
                }
            }
        }

        #endregion

        #region --- keyboard input ---

        bool leftkeyWasPressed = false;
        bool rightKeyWasPressed = false;
        bool upKeyWasPressed = false;
        bool spaceKeyWasPressed = false;
        bool shiftKeyWasPressed = false;

        public void ProcessKeyPresses()
        {
            if (Keyboard.GetState().IsKeyDown(Keys.Down))
            {
                _currentPiece.MoveDown();
            }

            ProcessLeftKeyPress();
            ProcessRightKeyPress();
            ProcessUpKeyPress();
            ProcessSpaceKeyPress();
            ProcessShiftKeyPress();
        }

        public void ProcessLeftKeyPress()
        {
            if (Keyboard.GetState().IsKeyDown(Keys.Left))
                leftkeyWasPressed = true;
            if (Keyboard.GetState().IsKeyUp(Keys.Left) && leftkeyWasPressed && !leftColliding)
            {
                  _currentPiece.MoveLeft();
                leftkeyWasPressed = false;
            }
        }

        public void ProcessRightKeyPress()
        {
            if (Keyboard.GetState().IsKeyDown(Keys.Right))
                rightKeyWasPressed = true;
            else if (Keyboard.GetState().IsKeyUp(Keys.Right) && rightKeyWasPressed && !rightColliding)
            {
                  _currentPiece.MoveRight();
                rightKeyWasPressed = false;
            }
        }

        public void ProcessUpKeyPress()
        {
            if (Keyboard.GetState().IsKeyDown(Keys.Up))
                upKeyWasPressed = true;
            else if (Keyboard.GetState().IsKeyUp(Keys.Up) && upKeyWasPressed)
            {
                _currentPiece.RotateRight();
                upKeyWasPressed = false;
            }
        }

        public void ProcessSpaceKeyPress()
        {
            if (Keyboard.GetState().IsKeyDown(Keys.Space))
            {
                spaceKeyWasPressed = true;
            }
            if (Keyboard.GetState().IsKeyUp(Keys.Space) && spaceKeyWasPressed)
            {
                SlamPieceDown();
                spaceKeyWasPressed = false;
            }
        }

        public void ProcessShiftKeyPress()
        {
            if (Keyboard.GetState().IsKeyDown(Keys.LeftShift))
                shiftKeyWasPressed = true;
            else if (Keyboard.GetState().IsKeyUp(Keys.LeftShift) && shiftKeyWasPressed)
            {
                
                if(_holdPiece == null)
                {
                    _holdPiece = _currentPiece;
                    _holdPiece.SetPieceLocationsForHoldPiece();
                    ReplaceSquares();
                    LoadRandomPiece();
                    PlacePieceOnHoldPieceGrid();
                    UpdateHoldPieceGrid();
                }
                else
                {
                    Piece tempPiece = _holdPiece;
                    _holdPiece = _currentPiece;
                    _holdPiece.SetPieceLocationsForHoldPiece();
                    ReplaceSquares();
                    _currentPiece = tempPiece;
                    _currentPiece.SetPieceLocationsForGrid();
                    PlacePieceOnGrid();
                    PlacePieceOnHoldPieceGrid();
                    UpdateHoldPieceGrid();
                }

                shiftKeyWasPressed = false;
            }
        }

        public void SlamPieceDown()
        {
            CollisionCheckDown();
            if (!bottomColliding && !_currentPiece.isStopped)
            {
                _currentPiece.MoveDown();
                SlamPieceDown();
            }
        }

        #endregion

        #region --- Collision Check ---

        private bool bottomColliding = false;
        private bool rightColliding = false;
        private bool leftColliding = false;

        private void CollisionCheck()
        {
            //CheckForLose();
            CollisionCheckDown();
            CollisionCheckLeft();
            CollisionCheckRight();
        }

        private void CheckForLose()
        {
            bool gameOver = false;
            for (int x = 0; x < _width; x++)
            {
                for (int y = 0; y < 2; y++)
                {
                    if (!grid[x, y].isEmpty)
                    {
                        gameOver = true;
                    }
                }
            }
            if (gameOver)
            {
                gameOver = false;
                foreach (Square square in _currentPiece.squares)
                {
                    Square nextSquare = grid[square.gridX, square.gridY + 1];
                    if (!nextSquare.isEmpty && _currentPiece.squares.Contains(nextSquare))
                        gameOver = true;
                }
            }
            if(gameOver)
                game.setGameOver(true);
        }

        private void CollisionCheckDown()
        {
            bottomColliding = false;

            foreach (Square square in _currentPiece.squares)
            {
                if (square.gridY + 1 < _height && !square.isEmpty)
                {
                    Square nextSquare = grid[square.gridX, square.gridY + 1];
                    if (!nextSquare.isEmpty && !_currentPiece.squares.Contains(nextSquare))
                    {
                        _currentPiece.isStopped = true;
                        bottomColliding = true;
                    }
                }
            }
        }

        private void CollisionCheckRight()
        {
            rightColliding = false;

            foreach (Square square in _currentPiece.squares)
            {
                if (square.gridX + 1 < _width && !square.isEmpty)
                {
                    Square nextSquare = grid[square.gridX+1, square.gridY];
                    if (!nextSquare.isEmpty && !_currentPiece.squares.Contains(nextSquare))
                    {
                        rightColliding = true;
                    }
                }
            }
        }

        private void CollisionCheckLeft()
        {
            leftColliding = false;

            foreach (Square square in _currentPiece.squares)
            {
                if (square.gridX > 0 && !square.isEmpty)
                {
                    Square nextSquare = grid[square.gridX-1, square.gridY];
                    if (!nextSquare.isEmpty && !_currentPiece.squares.Contains(nextSquare))
                    {
                        leftColliding = true;
                    }
                }
            }
        }

        #endregion

        #region --- Update ---

        //Logic for changing the grid so the pieces move
        public void Update(GameTime gt)
        {
            CollisionCheck();

            if (_currentPiece.isStopped)
            {
                CheckForLose();
                checkLineClear();
                LoadRandomPiece();
            }

            ReplaceSquares();

            ProcessKeyPresses();

            MovePieceDown();

            //not working properly
            //CheckForSpeedUp();

            ShowGhostPiece();

            PlacePieceOnGrid();

            numRowsCleared = 0;

            UpdateGrid();
        }

        public void UpdateGrid()
        {
            for (int i = 0; i < _width; i++)
            {
                for (int j = 0; j < _height; j++)
                {
                    Square square = grid[i, j];

                    if (square.gridY >= _topGridBuffer)
                    {
                        if (square.isEmpty)
                        {
                            square.Color = Color.White;
                            if (!square.isGhostSquare)
                                square.background = content.Load<Texture2D>(BLANK_SQUARE) as Texture2D;
                            else
                                square.background = content.Load<Texture2D>(GHOST_SQUARE) as Texture2D;
                        }
                        else
                        {
                            square.background = content.Load<Texture2D>(square.backgroundString) as Texture2D;
                        }
                        square.Position = new Vector2((square.gridX - _topGridBuffer) * Square.Width, Math.Abs(square.gridY - _topGridBuffer) * Square.Height);
                    }
                }
            }
        }

        #endregion

        #region --- Piece Movement ---

        public void ReplaceSquares()
        {
            foreach (Square square in _currentPiece.squares)
            {
                Square replaceSquare = new Square();
                replaceSquare.gridX = square.gridX;
                replaceSquare.gridY = square.gridY;
                replaceSquare.isEmpty = true;
                //if (ghostPieceY > 4)
                //{
                    grid[square.gridX, square.gridY] = replaceSquare;
                    grid[square.gridX, square.gridY + ghostPieceDiffY] = replaceSquare;
                //}
            }
        }

        public void PlacePieceOnGrid()
        {
            foreach (Square square in _currentPiece.squares)
            {
                if (square.gridY == _height && !square.isEmpty)
                    _currentPiece.isStopped = true;
                grid[square.gridX, square.gridY] = square;
            }
        }

        public void ShowGhostPiece()
        {
            bool showGhostPiece = false;
            ghostPieceDiffY = 0;
            if (!_currentPiece.isStopped)
            {
                while (!showGhostPiece)
                {
                    ghostPieceDiffY++;
                    foreach (Square square in _currentPiece.squares)
                    {
                        if (square.gridY + ghostPieceDiffY + 1 <= _height)
                        {
                            if ((square.gridY + ghostPieceDiffY + 1) >= _height)
                            {
                                showGhostPiece = true;
                                break;
                            }
                            else if (!grid[square.gridX, (square.gridY + ghostPieceDiffY + 1)].isEmpty)
                            {
                                if (!_currentPiece.squares.Contains(grid[square.gridX, (square.gridY + ghostPieceDiffY + 1)]))
                                {
                                    showGhostPiece = true;
                                    break;
                                }
                            }
                        }
                    }
                }
                if (showGhostPiece && ghostPieceDiffY > 4)
                {
                    foreach (Square square in _currentPiece.squares)
                    {
                        grid[square.gridX, square.gridY + ghostPieceDiffY] = new Square() { gridX = square.gridX, gridY = square.gridY + ghostPieceDiffY, backgroundString = GHOST_SQUARE, isGhostSquare = true, isEmpty = true };
                    }
                }
                else
                    ghostPieceDiffY = 0;
            }
        }

        public void MovePieceDown()
        {
            if (_numberOfCycles++ % _PIECE_SPEED == 0 && !bottomColliding)
            {
                _currentPiece.MoveDown();
            }
        }

        #endregion

        public void Draw(GameTime gt, SpriteBatch spriteBatch)
        {
            foreach (Square square in grid)
            {
                if (square.gridY > 1)
                    spriteBatch.Draw(square.background, new Vector2(square.Position.X + STARTING_GRID_X, square.Position.Y + STARTING_GRID_Y), square.Color);
            }
            foreach (Square square in nextPieceGrid)
            {
                spriteBatch.Draw(square.background, new Vector2(square.Position.X + STARTING_GRID_X + 270, square.Position.Y + STARTING_GRID_Y), square.Color);
            }
            foreach (Square square in holdPieceGrid)
            {
                spriteBatch.Draw(square.background, new Vector2(square.Position.X + STARTING_GRID_X - 289, square.Position.Y + STARTING_GRID_Y + 125), square.Color);
            }
        }

        private void LoadRandomPiece()
        {
            Random rand = new Random();
            _pieces = new List<Piece>() { new LPiece(), new JPiece(), new LinePiece(), new ZPiece(), new SPiece(), new TPiece(), new OPiece() };
            int randomPieceIndex = rand.Next(0, _pieces.Count);
            if (_nextPiece == null)
            {
                _nextPiece = _pieces[randomPieceIndex];
                randomPieceIndex = rand.Next(0, _pieces.Count);
                _pieces = new List<Piece>() { new LPiece(), new JPiece(), new LinePiece(), new ZPiece(), new SPiece(), new TPiece(), new OPiece() };
            }
            _currentPiece = _nextPiece;
            _nextPiece = _pieces[randomPieceIndex];
            _currentPiece.isStopped = false;
            PlacePieceOnNextPieceGrid();
            UpdateNextPieceGrid();
        }

        public void SpeedUp()
        {
            if(_PIECE_SPEED > 20)
                _PIECE_SPEED -= _SPEEDUP_INCREMENT;
        }

        public void ResetSpeed()
        {
            _PIECE_SPEED = INITSPEED;
        }

        private Piece GetNextPiece()
        {
            return _nextPiece;
        }

        private void checkLineClear()
        {
            for (int i = 0; i < _height; i++)
            {
                bool rowFull = true;
                for (int j = 0; j < _width; j++)
                {
                    if (grid[j, i].isEmpty)
                    {
                        rowFull = false;
                        break;
                    }
                }
                if (rowFull)
                {
                    deleteRow(i);
                }
            }
        }

        private void deleteRow(int row)
        {
            for (int i = 0; i < _width; i++)
            {
                grid[i, row] = new Square();
            }
            shiftRows(row);

            numRowsCleared += 1;
            increaseScore();
            
            game.rowsFinished++;
            if (game.rowsFinished == 10)
            {
                game.rowsFinished = 0;
                game.currentLevel.UpLevel();
                SpeedUp();
            }
            
        }

        private void shiftRows(int rowDeleted)
        {
            for (int i = rowDeleted; i >= 0; i--)
            {
                for (int j = 0; j < _width; j++)
                {
                    if (!grid[j, i].isEmpty)
                    {
                        Square replaceSquare = new Square();
                        grid[j, i].gridY++;
                        grid[j, i + 1] = grid[j, i];
                        grid[j, i] = replaceSquare;
                    }
                }
            }
        }

        private void increaseScore()
        {
            string type;
            if (numRowsCleared == 1)
                type = "single";
            else if (numRowsCleared == 2)
                type = "double";
            else if (numRowsCleared == 3)
                type = "triple";
            else if (numRowsCleared == 4)
            {
                numTetrisInRow += 1;
                type = "tetris";
            }
            else if (numTetrisInRow >= 2)
            {
                type = "doubleTetris";
                numTetrisInRow = 0;
            }
            else
                type = "single";

            switch (type)
            {
                case "single":
                    game.CurrentScore += 100 * game.currentLevel.CurrentLevel;
                    break;
                case "double":
                    game.CurrentScore += 300 * game.currentLevel.CurrentLevel;
                    break;
                case "triple":
                    game.CurrentScore += 500 * game.currentLevel.CurrentLevel;
                    break;
                case "tetris":
                    game.CurrentScore += 800 * game.currentLevel.CurrentLevel;
                    break;
                case "doubleTetris":
                    game.CurrentScore += 1600 * game.currentLevel.CurrentLevel;
                    break;
            }
        }
    }
}
